/*
 * C4.11.4.c
 *
 *  Created on: 2012/12/20
 *      Author: ming
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "rule_info.h"
#include "core.h"
#include "trace.h"
#include "TraceAst.h"

static const PSZ RuleID = "C4.11.4";

static const PSZ MESSAGE_ENG =
		"Data loss is possible due to implicit type conversion";
static const PSZ MESSAGE_JPN = "暗黙の型変換によりデータロスになる可能性がある";
static const PSZ MESSAGE_CHS = "隐式的型变换可能会导致数据丢失";

static const U2 RANK = MSG_RANK_ERR;

static PRULEOUT pRuleHead;
static PRULEOUT pRuleTail;

#define FUNC_PARA_NUM 32
static AST FUNC_PARA_TYPE[FUNC_PARA_NUM] = { 0 };
static INT gFuncParaNum = 0;

static VOID setFuncParaNum() {
	INT i;
	for (i = 0; i < FUNC_PARA_NUM; i++) {
		if (FUNC_PARA_TYPE[i] == 0) {
			break;
		}
		gFuncParaNum++;
	}
}

static BOOL isCheckType(AST typeRange, AST type) {
	if (typeRange <= 0 || type <= 0) {
		return FALSE;
	}
	if (!CTypeInfo.isBasicType(typeRange) || !CTypeInfo.isBasicType(type)) {
		return FALSE;
	}
	if (!CTypeInfo.isInteger(type)) {
		return FALSE;
	}
	return TRUE;
}

static BOOL isCheckFuncType(AST typeRange, AST type) {
	if (typeRange <= 0 || type <= 0) {
		return FALSE;
	}
	if (!CTypeInfo.isFunction(typeRange) || !CTypeInfo.isBasicType(type)) {
		return FALSE;
	}
	if (!CTypeInfo.isInteger(type)) {
		return FALSE;
	}
	return TRUE;
}

static BOOL isTypeInRange(AST typeRange, AST type) {
	INT rangeSize = 0;
	INT size = 0;
	if (CTypeInfo.isFloating(typeRange)) {
		return TRUE;
	}
	rangeSize = CTypeInfo.getTypeSize(typeRange);
	if (rangeSize <= 0) {
		return FALSE;
	}
	if (CTypeInfo.isUnSigned(typeRange)) {
		if (CTypeInfo.isSigned(type)) {
			return FALSE;
		}
		size = CTypeInfo.getTypeSize(type);
		if (size > rangeSize) {
			return FALSE;
		}
	} else if (CTypeInfo.isSigned(typeRange)) {
		if (CTypeInfo.isSigned(type)) {
			size = CTypeInfo.getTypeSize(type);
			if (size > rangeSize) {
				return FALSE;
			}
		} else {
			size = CTypeInfo.getTypeSize(type);
			if (size >= rangeSize) {
				return FALSE;
			}
		}
	}
	return TRUE;
}

static INT checkAssign(AST exp) {
	AST leftExp = CExpression.getLeftNode(exp);
	AST rightExp = CExpression.getRightNode(exp);
	AST leftType = CExpression.getTypeInfo(leftExp);
	AST rightType = CExpression.getTypeInfo(rightExp);
	if (isCheckType(leftType, rightType)) {
		if (isTypeInRange(leftType, rightType)) {
			return RULE_RESULT_OK;
		} else {
			return RULE_RESULT_NG;
		}
	}
	return RULE_RESULT_OK;
}

static BOOL isFuncParaTypeOK(AST exp) {
	U4 expKind = CExpression.getOpKind(exp);
	AST tmpExp = 0;
	AST type = 0;
	AST typeRange = 0;
	if (CExpressionOpKind.isArgumentDelimiter(expKind)) {
		tmpExp = CExpression.getRightNode(exp);
		if (!isFuncParaTypeOK(tmpExp)) {
			return FALSE;
		} else {
			tmpExp = CExpression.getLeftNode(exp);
			return isFuncParaTypeOK(tmpExp);
		}
	} else {
		type = CExpression.getTypeInfo(exp);
		if (gFuncParaNum < 1) {
			return FALSE;
		}
		typeRange = FUNC_PARA_TYPE[gFuncParaNum - 1];
		gFuncParaNum--;
		if (isCheckType(typeRange, type)) {
			if (isTypeInRange(typeRange, type)) {
				return TRUE;
			} else {
				return FALSE;
			}
		} else {
			return TRUE;
		}
	}
	return TRUE;
}

static VOID loadAllFuncPara(AST chain) {
	AST symbol = 0;
	INT cnt = 0;
	while (chain) {
		if (!CUniteChain.isSymbolInfo(chain)) {
			return;
		}
		symbol = CUniteChain.getSymbol(chain);
		FUNC_PARA_TYPE[cnt] = CSymbol.getType(symbol);
		cnt++;
		chain = CUniteChain.getNextOne(chain);
	}
}

static INT checkFuncCall(AST exp) {
	AST leftExp = CExpression.getLeftNode(exp);
	AST leftType = CExpression.getTypeInfo(leftExp);
	AST chain = CTypeInfo.getMemberChain(leftType);
	AST rightExp = CExpression.getRightNode(exp);

	loadAllFuncPara(chain);
	setFuncParaNum();

	if (!isFuncParaTypeOK(rightExp)) {
		return RULE_RESULT_NG;
	}
	return RULE_RESULT_OK;
}

static AST getFuncType(AST stmt) {
	AST parent = 0;
	if (CStatement.isParentSymbol(stmt)) {
		parent = CStatement.parent_GetSymbol(stmt);
		if (CSymbol.isFunction(parent)) {
			return CSymbol.getType(parent);
		} else {
			return 0;
		}
	} else if (CStatement.isParentStmt(stmt)) {
		parent = CStatement.parent_GetStmt(stmt);
		return getFuncType(parent);
	}
	return 0;
}

static INT checkReturn(AST stmt) {
	AST exp = CStatement.return_getExp(stmt);
	AST type = CExpression.getTypeInfo(exp);
	AST typeRange = getFuncType(stmt);
	if (isCheckFuncType(typeRange, type)) {
		if (isTypeInRange(typeRange, type)) {
			return RULE_RESULT_OK;
		} else {
			return RULE_RESULT_NG;
		}
	}
	return RULE_RESULT_OK;
}

/******************************************************************
 * main logic
 *******************************************************************/
static S4 RuleMain(U2 kind, AST index, U4 event) {
	INT result = RULE_RESULT_OK;
	PRULEOUT newNode = NULL;
	PRULEOUT tmpNode = NULL;
	PRULEOUT tmpNode1 = NULL;

	AST codeScp = 0;
	U4 expKind = 0;

	if ((kind == TR_KIND_EXPRESSION) && (event & EVT_EXPRESSION)) {
		expKind = CExpression.getOpKind(index);
		if (CExpressionOpKind.isAssignmentOp(expKind)) {
			result = checkAssign(index);
		} else if (CPostfixOpKind.isFunctionCall(expKind)) {
			result = checkFuncCall(index);
		} else {
			return EXIT_SUCCESS;
		}
	} else if ((kind == TR_KIND_STATEMENT) && (event & EVT_STATEMENT)) {
		if (CStatement.isReturn(index)) {
			result = checkReturn(index);
		} else {
			return EXIT_SUCCESS;
		}
	} else {
		return EXIT_SUCCESS;
	}

	if (result == RULE_RESULT_OK) {
		return EXIT_SUCCESS;
	}

	/*re-checked?*/
	tmpNode = pRuleHead;
	while (tmpNode != NULL ) {
		if (tmpNode->indexInfo == index) {
			if (result > RULE_RESULT_OK) {
				result = RULE_RESULT_OK;
			} else {
				if (tmpNode == pRuleHead) {
					pRuleHead = pRuleHead->next;
					free(tmpNode);
				} else if (tmpNode == pRuleTail) {
					tmpNode1->next = NULL;
					pRuleTail = tmpNode1;
					free(tmpNode);
				} else {
					tmpNode1->next = tmpNode->next;
					free(tmpNode);
				}
			}
			break;
		}
		tmpNode1 = tmpNode;
		tmpNode = tmpNode->next;
	}

	/*make result struct*/
	if (result > RULE_RESULT_OK) {
		newNode = (PRULEOUT) malloc(sizeof(RULEOUT));
		if (pRuleHead == NULL ) {
			pRuleHead = newNode;
		}

		if (kind == TR_KIND_EXPRESSION) {
			codeScp = CExpression.getCodeScope(index);
		} else if (kind == TR_KIND_STATEMENT) {
			codeScp = CStatement.getCodeScope(index);
		}
		newNode->indexInfo = index;
		newNode->fileIndex = CCodeScope.getFileIndex(codeScp);
		newNode->beginLine = CCodeScope.getBeginLine(codeScp);
		newNode->beginCol = CCodeScope.getBeginLineOffset(codeScp);
		newNode->endLine = CCodeScope.getEndLine(codeScp);
		newNode->endCol = CCodeScope.getEndLineOffset(codeScp);
		newNode->ruleID = RuleID;
		newNode->errRank = RANK;

		sprintf(newNode->msgBuf_eng, MESSAGE_ENG);
		sprintf(newNode->msgBuf_chs, MESSAGE_CHS);
		sprintf(newNode->msgBuf_jpn, MESSAGE_JPN);

		newNode->rfileIndex = 0;
		newNode->rlocLine = 0;
		newNode->next = NULL;

		if (pRuleTail != NULL ) {
			pRuleTail->next = newNode;
		}

		pRuleTail = newNode;
	}

	return EXIT_SUCCESS;
}

VOID C4_11_4_register(VOID) {
	PRULEMAN * pCurRule = &stRuleManager;
	PRULEMAN newRuleMan = (PRULEMAN) malloc(sizeof(RULEMAN));
	memset(newRuleMan, 0, sizeof(RULEMAN));
	newRuleMan->ruleID = RuleID;
	newRuleMan->ruleSwitch = OFF;
	newRuleMan->headOut = &pRuleHead;
	newRuleMan->tailOut = &pRuleTail;
	newRuleMan->ruleMain_ast = RuleMain;
	if (stRuleManager == NULL ) {
		stRuleManager = newRuleMan;
	} else {
		while ((*pCurRule)->nextRule != NULL ) {
			pCurRule = &((*pCurRule)->nextRule);
		}
		(*pCurRule)->nextRule = newRuleMan;
	}
}

